#include "stdafx.h"
#include "RenderContext.h"
#include "OglProgram.h"

CRenderContextBase::CRenderContextBase()
: m_bStartup(FALSE)
, m_hWnd(NULL)
, m_hDC(NULL)
, m_hRC(NULL)
{
}

CRenderContextBase::~CRenderContextBase()
{
}

HRESULT CRenderContextBase::Startup(HWND hPreviewWnd)
{
	if(m_bStartup)return HRESULT_FROM_WIN32(ERROR_INVALID_STATE);

	HRESULT hr = S_OK;

	m_hWnd = hPreviewWnd;
	
	GLuint uiPixelFormat;
	PIXELFORMATDESCRIPTOR pfd;
	::ZeroMemory( &pfd, sizeof(PIXELFORMATDESCRIPTOR) );

	pfd.nSize      = sizeof(PIXELFORMATDESCRIPTOR);
	pfd.nVersion   = 1;
	pfd.dwFlags    = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
	pfd.iPixelType = PFD_TYPE_RGBA;
	pfd.cColorBits = 32;
	pfd.cDepthBits = 24;
	pfd.cStencilBits = 8;
	pfd.iLayerType = PFD_MAIN_PLANE;

	m_hDC = ::GetDC( m_hWnd );
	uiPixelFormat = ::ChoosePixelFormat( m_hDC, &pfd );
	::SetPixelFormat( m_hDC, uiPixelFormat, &pfd);
	
	m_hRC = wglCreateContext( m_hDC );

	if(!wglMakeCurrent(m_hDC, m_hRC))
	{
		assert(FALSE);

		m_hWnd = NULL;
		m_hDC = NULL;
		m_hRC = NULL;

		DWORD dwError = ::GetLastError();
		return HRESULT_FROM_WIN32(dwError);
	}
	
	//init glew and check version of opengl
	GLenum uiError = glewInit();

	exstring strTrace;
	strTrace.format("opengl version: %s\n", (LPCSTR)glGetString(GL_VERSION));
	::OutputDebugStringA(strTrace);

	if(!GL_VERSION_3_0)
	{
		assert(FALSE);

		m_hWnd = NULL;
		m_hDC = NULL;
		m_hRC = NULL;

		return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
	}

	m_bStartup = TRUE;

	return S_OK;
}

void CRenderContextBase::Shutdown()
{
	if(!m_bStartup)return;

	if(m_hRC)
	{
		wglDeleteContext(m_hRC);
		m_hRC = NULL;
	}

	if(m_hDC)
	{
		::ReleaseDC(m_hWnd, m_hDC);
		m_hDC = NULL;
	}

	m_hWnd = NULL;
}

HRESULT CRenderContextBase::Begin()
{
	//calculate display area in canvas
	RECT rcCanvas;
	int nCanvasWidth, nCanvasHeight;
	::GetClientRect(m_hWnd, &rcCanvas);
	nCanvasWidth = rcCanvas.right - rcCanvas.left;
	nCanvasHeight = rcCanvas.bottom - rcCanvas.top;

	//////////////////////////////////////////////
	//set environment
	if(!wglMakeCurrent(m_hDC, m_hRC))
	{
		assert(FALSE);
		DWORD dwError = ::GetLastError();
		return HRESULT_FROM_WIN32(dwError);
	}

	//save current state
	glPushAttrib(GL_ALL_ATTRIB_BITS);
	glPushClientAttrib(GL_CLIENT_ALL_ATTRIB_BITS);

	//setup render environment
	glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
	OGL_ASSERT_OK();

	glViewport(0, 0, nCanvasWidth, nCanvasHeight);
	OGL_ASSERT_OK();

	return S_OK;
}

HRESULT CRenderContextBase::End()
{
	glPopClientAttrib();
	glPopAttrib();

	OGL_ASSERT_OK();
	
	return S_OK;
}

HRESULT CRenderContextBase::SwapBffers()
{
	wglSwapLayerBuffers(m_hDC, WGL_SWAP_MAIN_PLANE);

	return S_OK;
}


//GLSL Version	OpenGL Version
//1.10.59		2.0
//1.20.8		2.1
//1.30.10		3.0
//1.40.08		3.1
//1.50.11		3.2
//3.30.6		3.3
//4.00.9		4.0
//4.10.6		4.1
//4.20.11		4.2
//4.30.8		4.3

LPCSTR g_szRenderVertexShader = 
	"#version 130\n"
	"uniform mat4 world_view_proj_matrix;"
	"in vec4 position;"
	"in vec4 diffuse;"
	"out vec4 outdiff;"
	"void main()"
	"{"
	"	gl_Position = world_view_proj_matrix * position;"
	"	outdiff = diffuse;"
	"}";

LPCSTR g_szRenderFragmentShader =
	"#version 130\n"
	"in vec4 outdiff;"
	"void main()"
	"{"
	"	gl_FragColor = outdiff;"
	"}";

CRenderContext::CRenderContext()
	: m_pRenderProgram(NULL)
	, m_nPositionAttribLocation(-1)
	, m_nDiffuseAtrribLocation(-1)
{
}

CRenderContext::~CRenderContext()
{
}

HRESULT CRenderContext::Startup(HWND hPreviewWnd)
{
	HRESULT hr = CRenderContextBase::Startup(hPreviewWnd);

	if(SUCCEEDED(hr))
	{
		m_pRenderProgram = new COglProgram();

		hr = m_pRenderProgram->LoadProgram(
			g_szRenderVertexShader, g_szRenderFragmentShader);
		assert(SUCCEEDED(hr));

		m_nPositionAttribLocation = glGetAttribLocation(*m_pRenderProgram, "position");
		m_nDiffuseAtrribLocation = glGetAttribLocation(*m_pRenderProgram, "diffuse");
	}

	return hr;
}

void CRenderContext::Shutdown()
{
	if(!IsStart())return;

	if(m_pRenderProgram)
	{
		delete m_pRenderProgram;
		m_pRenderProgram = NULL;
	}

	CRenderContextBase::Shutdown();
}

HRESULT CRenderContext::Begin(const glm::mat4& stWorldViewProj)
{
	HRESULT hr = CRenderContextBase::Begin();
	if(FAILED(hr))return hr;
	
	//set program and uniform variables in it.
	glUseProgram(*m_pRenderProgram);
	OGL_ASSERT_OK();
	GLint nWorldViewProjMatrix = glGetUniformLocation(*m_pRenderProgram, "world_view_proj_matrix");
	glUniformMatrix4fv(nWorldViewProjMatrix, 1, GL_FALSE, &stWorldViewProj[0][0]);
	OGL_ASSERT_OK();

	return S_OK;
}

HRESULT CRenderContext::End()
{
	//unload program
	glUseProgram(0);

	return CRenderContextBase::End();
}
